/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_KNIFE_C_DOWNLOAD_INTERCEPTOR_OH_H
#define IMAGE_KNIFE_C_DOWNLOAD_INTERCEPTOR_OH_H

#include "imageknife_task_internal.h"
#include "interceptor.h"
#include "imageknife_request.h"
#include "imageknife_data_napi.h"
#include <future>
#include <stack>
#include "securec.h"
#include "imageknife_internal.h"
#include "hitrace/trace.h"

namespace ImageKnifePro {

class DownloadInterceptorDefault : public LoadInterceptor {
public:
    DownloadInterceptorDefault()
    {
        name = "Default DownloadInterceptor";
    }

    bool Resolve(std::shared_ptr<ImageKnifeTask> task) override
    {
        std::string url;
        bool result = false;
        static std::atomic<int> idx =  1;
        int traceId = idx;
        idx++;

        OH_HiTrace_StartAsyncTrace (name.c_str(), traceId);
        if (task->GetImageSource()->GetString(url)) {
            result = LoadImageFromUrl(url, task);
        }
        OH_HiTrace_FinishAsyncTrace(name.c_str(), traceId);

        return result;
    }

    void Cancel(std::shared_ptr<ImageKnifeTask> task) override
    {
        auto taskInternal = std::static_pointer_cast<ImageKnifeTaskInternal>(task);
        std::lock_guard<std::mutex> guard(taskInternal->cancelMutex_);
        if (taskInternal->rcpRequest != nullptr && taskInternal->rcpSession != nullptr) {
            uint32_t errorCode = HMS_Rcp_CancelRequest(taskInternal->rcpSession, taskInternal->rcpRequest);
            if (!errorCode) {
                task->cancelInfo = "Cancel Download Success";
            } else {
                task->EchoError("Cancel Download Failed, error code: " + std::to_string(errorCode));
            }
        }
    }

private:
    // Rcp 异步回调数据
    struct CallbackData {
        uint32_t errCode = 0;
        int httpCode = RCP_OK;
        std::promise<std::string> promise;
        Rcp_Response* response = nullptr;
    };

    std::stack<Rcp_Session *> sessionStack_;
    std::mutex sessionLock_;

    Rcp_Session *GetRcpSession();
    void RecycleSession(Rcp_Session *session);

    Rcp_Headers *CreateRcpHeaders(const std::multimap<std::string, std::string> &head)
    {
        if (head.size() == 0) {
            return nullptr;
        }
        Rcp_Headers *rcpHeaders = HMS_Rcp_CreateHeaders();
        for (auto it = head.begin(); it != head.end(); it++) {
            HMS_Rcp_SetHeaderValue(rcpHeaders, it->first.c_str(), it->second.c_str());
        }
        return rcpHeaders;
    }

    bool LoadImageFromUrl(std::string url, std::shared_ptr<ImageKnifeTask> task)
    {
        auto taskInternal = std::static_pointer_cast<ImageKnifeTaskInternal>(task);
        taskInternal->rcpRequest = HMS_Rcp_CreateRequest(url.c_str());
        if (taskInternal->rcpRequest == nullptr) {
            task->EchoError("Create Rcp Request Failed");
            return false;
        }
        ConfigHttpRequest(taskInternal);

        taskInternal->rcpSession = GetRcpSession();
        if (taskInternal->rcpSession == nullptr) {
            task->EchoError("Get Session Failed");
            std::lock_guard<std::mutex> guard(taskInternal->cancelMutex_);
            DestroyConfiguration(taskInternal);
            HMS_Rcp_DestroyRequest(taskInternal->rcpRequest);
            taskInternal->rcpRequest = nullptr;
            return false;
        }

        CallbackData data;
        Rcp_ResponseCallbackObject callback = {ResponseCallback, &data};
        uint32_t errCode = HMS_Rcp_Fetch(taskInternal->rcpSession, taskInternal->rcpRequest, &callback);
        if (errCode) {
            data.promise.set_value("HMS_Rcp_Fetch error: " + std::to_string(errCode));
        }
        // 等待异步下载过程结束
        std::string info = data.promise.get_future().get();

        // 释放资源
        taskInternal->cancelMutex_.lock();
        DestroyConfiguration(taskInternal);
        HMS_Rcp_DestroyRequest(taskInternal->rcpRequest);
        RecycleSession(taskInternal->rcpSession);
        taskInternal->rcpRequest = nullptr;
        taskInternal->rcpSession = nullptr;
        taskInternal->cancelMutex_.unlock();

        if (data.response != nullptr) {
            task->httpCode = data.response->statusCode;
            taskInternal->rcpResponse = data.response;

            // 提供删除器避免错误析构下载数据buffer
            Rcp_Response *rcpResponse = data.response;
            auto deleter = [rcpResponse](void *) {
                rcpResponse->destroyResponse(rcpResponse);
            };
            taskInternal->product.imageBuffer = std::shared_ptr<uint8_t>((uint8_t *)data.response->body.buffer,
                                                                         deleter);
            taskInternal->product.imageLength = data.response->body.length;
            return true;
        } else if (!info.empty()) { // 成功取消下载info可能为空
            task->EchoError(info);
        }

        return false;
    }

    void DestroyConfiguration(std::shared_ptr<ImageKnifeTaskInternal> task)
    {
        if (task->rcpRequest->headers != nullptr) {
            HMS_Rcp_DestroyHeaders(task->rcpRequest->headers);
        }
        auto config = task->rcpRequest->configuration;
        if (config != nullptr) {
            if (config->securityConfiguration.certificate.filePath != nullptr) {
                free(config->securityConfiguration.certificate.filePath);
                config->securityConfiguration.certificate.filePath = nullptr;
            }
            delete config;
        }
        task->rcpRequest->configuration = nullptr;
    }

    void ConfigHttpRequest(std::shared_ptr<ImageKnifeTaskInternal> task)
    {
        task->rcpRequest->headers = CreateRcpHeaders(task->GetImageKnifeRequest()->
            GetImageKnifeOption()->httpHeaders);
        if (task->rcpRequest->headers == nullptr) {
            task->rcpRequest->headers = CreateRcpHeaders(ImageKnifeInternal::GetInstance().GetGlobalHttpHeaders());
        }

        // 设置为Get请求
        task->rcpRequest->method = RCP_METHOD_GET;
        auto option = task->GetImageKnifeRequest()->GetImageKnifeOption();
        if (option == nullptr) {
            return;
        }

        Rcp_Configuration *config = new Rcp_Configuration;
        (void)memset_s(config, sizeof(Rcp_Configuration), 0, sizeof(Rcp_Configuration));

        // 设置连接超时时间
        if (option->httpOption.connectTimeout > 0) {
            config->transferConfiguration.timeout.connectMs = option->httpOption.connectTimeout;
        }

        // 设置传输超时时间
        if (option->httpOption.readTimeout > 0) {
            config->transferConfiguration.timeout.transferMs = option->httpOption.readTimeout;
        }

        // 设置 CA证书
        if (option->httpOption.caPath.length() > 0) {
            int length = option->httpOption.caPath.length() + 1;
            config->securityConfiguration.certificate.filePath = (char *)malloc(length);
            memset_s(config->securityConfiguration.certificate.filePath, length, 0x00, length);
            strcpy_s(config->securityConfiguration.certificate.filePath, length,
                     option->httpOption.caPath.c_str());
        }

        // 设置下载进度监听回调
        if (option->progressListener != nullptr && task->GetImageRequestType() == ImageRequestType::MAIN_SRC) {
            config->tracingConfiguration.httpEventsHandler.onDownloadProgress = {.callback = OnProgressCallback,
                .usrObject = &(option->progressListener)};
        }
        config->transferConfiguration.autoRedirect = true;
        task->rcpRequest->configuration = config;
    }

    static void OnProgressCallback(void* userObject, uint64_t totalSize, uint64_t transferredSize)
    {
        if (totalSize > 0) {
            std::function<void(double)>* userFunc = static_cast<std::function<void(double)>*>(userObject);
            (*userFunc)(static_cast<double>(transferredSize) / totalSize);
        }
    }

    static void ResponseCallback(void *usrCtx, Rcp_Response *response, uint32_t errCode)
    {
        const int httpDownloadCanceled = 1007900992;
        CallbackData *data = static_cast<CallbackData*>(usrCtx);
        data->errCode = errCode;
        std::string errorInfo;
        if (errCode) {
            if (errCode != httpDownloadCanceled) {
                errorInfo = "HttpFetch HMS_Rcp_Fetch error:" + std::to_string(errCode);
            }
        }

        if (response != NULL) {
            data->httpCode = response->statusCode;
            if (response->statusCode != RCP_OK) {
                errorInfo = "HttpFetch response error, statusCode: " + std::to_string(response->statusCode);
                response->destroyResponse(response);
            } else {
                data->response = response;
            }
        }

        data->promise.set_value(errorInfo);
    }
};

};

#endif // IMAGE_KNIFE_C_DOWNLOAD_INTERCEPTOR_OH_H
